#!/usr/bin/python

from types import *
import re
try:
    import psyco # Optional, 2.5x improvement in speed
    psyco.full()
except ImportError:
    pass

class Bencoding:
    decimal_match = re.compile('\d')
    def _listToBen (self, list):
        benList = "l"
        for element in list:
            benList = benList + self.makeBen(element)
        benList = benList + "e"
        return benList

    def _dictToBen(self, dict):
        benDict = "d"
        helperList = list()
        for entry in dict:
            if type(entry) != StringType:
                raise "Hash key must be string"
            helperList.append(entry)
        helperList.sort()
        for element in helperList:
            benDict = benDict + self.makeBen(element) +\
                self.makeBen(dict[element])
        benDict = benDict + "e"
        return benDict

    def makeBen(self, element):
        '''Main function to bencode data'''
        if type(element) == StringType:
            benValue = str(len(element)) + ":" + element
        if type(element) == IntType or type(element) == LongType:
            benValue = "i" + str(element) + "e"
        if type(element) == ListType:
            benValue = self._listToBen(element)
        if type(element) == DictType:
            benValue = self._dictToBen(element)
        return benValue

#Decoding code from Hackeron @ wiki.theory.org
    def makeBin(self, data):
        '''Main function to decode bencoded data'''
        chunks = list(data)
        chunks.reverse()
        root = self._dechunk(chunks)
        return root

    def _dechunk(self, chunks):
        item = chunks.pop()
        if (item == 'd'): 
            item = chunks.pop()
            hash = {}
            while (item != 'e'):
                chunks.append(item)
                key = self._dechunk(chunks)
                hash[key] = self._dechunk(chunks)
                item = chunks.pop()
            return hash
        elif (item == 'l'):
            item = chunks.pop()
            list = []
            while (item != 'e'):
                chunks.append(item)
                list.append(self._dechunk(chunks))
                item = chunks.pop()
            return list
        elif (item == 'i'):
            item = chunks.pop()
            num = ''
            while (item != 'e'):
                num  += item
                item = chunks.pop()
            return int(num)
        elif (self.decimal_match.findall(item)):
            num = ''
            while self.decimal_match.findall(item):
                num += item
                item = chunks.pop()
            line = ''
            for i in range(1, int(num) + 1):
                line += chunks.pop()
            return line
        raise "Invalid input!"
